package com.zxing.slice;

import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_PREVIEW;
import static ohos.media.camera.params.Metadata.FlashMode.*;

import com.zxing.ResourceTable;
import com.zxing.utils.Log;
import com.zxing.view.FinderSetting;
import com.zxing.view.ViewfinderView;

import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.Text;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.graphics.Surface;
import ohos.agp.graphics.SurfaceOps;
import ohos.app.Environment;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.RawFileEntry;
import ohos.media.camera.CameraKit;
import ohos.media.camera.device.*;
import ohos.media.image.Image;
import ohos.media.image.ImageReceiver;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.ImageFormat;
import ohos.media.image.common.Rect;
import ohos.media.player.Player;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.UUID;

/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 二维码扫描页面 slice
 */
public class CaptureAbilitySlice extends AbilitySlice
        implements ImageReceiver.IImageArrivalListener, Component.ClickedListener {
    private static final int SCREEN_WIDTH = 2340;

    private static final int SCREEN_HEIGHT = 1060;

    private static final int IMAGE_RCV_CAPACITY = 5;

    private SurfaceProvider surfaceProvider;

    private ImageReceiver imageReceiver;

    private Camera cameraDevice;

    private Surface previewSurface;

    private EventHandler creamEventHandler;

    private Text txtClose;

    Text txtOpenFlash;
    private boolean isShowTopBar = true;
    private DependentLayout topBar;
    private boolean isSound = true;
    private boolean isOpen = true;

    private FrameConfig.Builder framePreviewConfigBuilder;

    public CaptureAbilitySlice(CodeUtils.AnalyzeCallback analyzeCallback) {
        this.analyzeCallback = analyzeCallback;
    }

    public CaptureAbilitySlice(CodeUtils.AnalyzeCallback analyzeCallback, FinderSetting setting) {
        this.analyzeCallback = analyzeCallback;
        this.setting = setting;
    }

    public CaptureAbilitySlice(FinderSetting setting) {

        this.setting = setting;
    }

    private FinderSetting setting;

    public CaptureAbilitySlice() {}

    private CodeUtils.AnalyzeCallback analyzeCallback;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_capture);

        setting = intent.getSerializableParam("setting");
        isShowTopBar = intent.getBooleanParam("isShowTopBar", true);
        isSound = intent.getBooleanParam("isSound", true);

        txtOpenFlash = (Text) findComponentById(ResourceTable.Id_txt_open_flash);
        txtClose = (Text) findComponentById(ResourceTable.Id_txt_close);
        topBar = (DependentLayout) findComponentById(ResourceTable.Id_top_bar);

        topBar.setVisibility(isShowTopBar ? Component.VISIBLE : Component.HIDE);
        getWindow().setTransparent(true);
        if (creamEventHandler == null) {
            creamEventHandler = new EventHandler(EventRunner.create("CameraBackground"));
        }
        if (isSound) {
            initBeepSound();
        }
        initSurface();

        txtOpenFlash.setClickedListener(this);
        txtClose.setClickedListener(this);
    }

    @Override
    public void onClick(Component component) {
        if (component.getId() == ResourceTable.Id_txt_open_flash) {
            if (isOpen) {
                isOpen = false;
                txtOpenFlash.setText("关闭闪光灯");
                openFlashlight();
            } else {
                isOpen = true;
                txtOpenFlash.setText("打开闪光灯");
                closeFlashlight();
            }
        } else if (component.getId() == ResourceTable.Id_txt_close) {
            terminate();
        }
    }
    /**
     * 打开或关闭闪关灯
     *
     * @param on
     */
    protected void switchLight(boolean on) {
        if (on) {
            openFlashlight();
        } else {
            closeFlashlight();
        }
    }

    void openFlashlight() {
        framePreviewConfigBuilder.setFlashMode(FLASH_ALWAYS_OPEN);
        cameraDevice.triggerLoopingCapture(framePreviewConfigBuilder.build());
    }

    void closeFlashlight() {
        framePreviewConfigBuilder.setFlashMode(FLASH_CLOSE);
        cameraDevice.triggerLoopingCapture(framePreviewConfigBuilder.build());
    }

    private ViewfinderView viewfinderView;

    private void initSurface() {
        if (surfaceProvider != null) {
            return;
        }
        DependentLayout dependentLayout = (DependentLayout) findComponentById(ResourceTable.Id_surface_container);
        surfaceProvider = new SurfaceProvider(this);
        dependentLayout.addComponent(
                surfaceProvider,
                new DependentLayout.LayoutConfig(
                        ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT));
        if (setting != null) {
            viewfinderView = new ViewfinderView(this, setting);
        } else {
            viewfinderView = new ViewfinderView(this);
        }
        dependentLayout.addComponent(
                viewfinderView,
                new DependentLayout.LayoutConfig(
                        ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT));
        surfaceProvider.pinToZTop(false);
        if (surfaceProvider != null) {
            surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack());
        }
    }

    private boolean isAnalyze = false;

    @Override
    public void onImageArrival(ImageReceiver imageReceiver) {
        try {
            Image image = imageReceiver.readLatestImage();
            if (image == null) {
                return;
            }
            if (isAnalyze) {
                image.release();
                return;
            }
            isAnalyze = true;
            Image.Component component = image.getComponent(ImageFormat.ComponentType.JPEG);
            byte[] bytes = new byte[component.remaining()];
            ByteBuffer buffer = component.getBuffer();
            buffer.get(bytes);
            ImageSource imageSource = ImageSource.create(bytes, new ImageSource.SourceOptions());

            ImageSource.DecodingOptions options = new ImageSource.DecodingOptions();
            options.rotateDegrees = 90f;
            options.desiredRegion =
                    new Rect(
                            viewfinderView.getFramingRect().top,
                            viewfinderView.getFramingRect().left,
                            viewfinderView.getFramingRect().getHeight(),
                            viewfinderView.getFramingRect().getWidth());
            PixelMap map = imageSource.createPixelmap(options);
            saveImage(bytes);
            String result = CodeUtils.parseInfoFromBitmap(map);
            Log.debug("result=" + result);
            if (result != null) {
                showMap(map, result);
                return;
            }
            image.release();
            isAnalyze = false;

        } catch (Exception e) {
            Log.debug(e.getMessage());
        }
    }

    private int time = 0;

    private static final String IMG_FILE_PREFIX = "IMG_";

    private static final String IMG_FILE_TYPE = ".jpg";

    private void saveImage(byte[] bitmapPixels) {
        FileOutputStream output = null;
        try {
            String fileName = IMG_FILE_PREFIX + UUID.randomUUID() + IMG_FILE_TYPE;
            File targetFile = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName);
            output = new FileOutputStream(targetFile);
            output.write(bitmapPixels);
        } catch (IOException e) {
            Log.debug("saveImage ERROR =" + e.getMessage());
        } finally {
            if (output != null) {
                try {
                    output.close();
                } catch (IOException e) {
                    Log.debug(e.getMessage());
                }
            }
        }
    }

    private Player player;

    private void initBeepSound() {
        try {
            player = new Player(this);

            String data = getResourceManager().getMediaPath(ResourceTable.Media_beep);
            RawFileEntry fileEntry = getResourceManager().getRawFileEntry(data);
            player.setSource(fileEntry.openRawFileDescriptor());

            player.setVolume(0.10f);
            player.prepare();
        } catch (Exception e) {
            Log.error(e.getMessage());
        }
    }

    private void showMap(PixelMap map, String result) {
        if (isSound && player != null) {
            player.play();
        }
        if (analyzeCallback != null) {
            analyzeCallback.onAnalyzeSuccess(map, result);
        }
        Intent intent = new Intent();
        intent.setParam("result", result);
        setResult(intent);
        terminate();
    }

    /**
     * SurfaceCallBack
     *
     * @since 2020-09-03
     */
    class SurfaceCallBack implements SurfaceOps.Callback {
        @Override
        public void surfaceCreated(SurfaceOps callbackSurfaceOps) {
            if (callbackSurfaceOps != null) {
                callbackSurfaceOps.setFixedSize(SCREEN_WIDTH,SCREEN_HEIGHT );
            }
            openCamera();
        }

        @Override
        public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) {}

        @Override
        public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) {}
    }

    private void openCamera() {
        imageReceiver = ImageReceiver.create(SCREEN_WIDTH, SCREEN_HEIGHT, ImageFormat.JPEG, IMAGE_RCV_CAPACITY);
        imageReceiver.setImageArrivalListener(this);
        CameraKit cameraKit = CameraKit.getInstance(getApplicationContext());
        String[] cameraList = cameraKit.getCameraIds();
        String cameraId = null;
        for (String id : cameraList) {
            if (cameraKit.getCameraInfo(id).getFacingType() == CameraInfo.FacingType.CAMERA_FACING_BACK) {
                cameraId = id;
            }
        }
        if (cameraId == null) {
            return;
        }
        CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl();
        cameraKit.createCamera(cameraId, cameraStateCallback, creamEventHandler);
    }

    class CameraStateCallbackImpl extends CameraStateCallback {
        CameraStateCallbackImpl() {}

        @Override
        public void onCreated(Camera camera) {
            Log.debug("create camera onCreated");
            Log.debug("surfaceProvider==null  =" + (surfaceProvider));
            if (surfaceProvider == null) {
                return;
            }
            previewSurface = surfaceProvider.getSurfaceOps().get().getSurface();
            if (previewSurface == null) {
                Log.debug("create camera filed, preview surface is null");
                return;
            }
            // Wait until the preview surface is created.
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                Log.debug("Waiting to be interrupted");
            }
            CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder();
            cameraConfigBuilder.addSurface(previewSurface);
            cameraConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
            camera.configure(cameraConfigBuilder.build());
            cameraDevice = camera;

            framePreviewConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_PREVIEW);
            framePreviewConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
            framePreviewConfigBuilder.addSurface(previewSurface);
            camera.triggerLoopingCapture(framePreviewConfigBuilder.build());
        }

        @Override
        public void onConfigured(Camera camera) {
            Log.debug("onConfigured....");

            framePreviewConfigBuilder.setFlashMode(FLASH_OPEN);
            framePreviewConfigBuilder.addSurface(previewSurface);

            camera.triggerLoopingCapture(framePreviewConfigBuilder.build());
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (cameraDevice != null) {
            framePreviewConfigBuilder = null;
            try {
                cameraDevice.release();
                cameraDevice = null;
                surfaceProvider.clearFocus();
                surfaceProvider.removeFromWindow();
                surfaceProvider = null;
            } catch (Exception e) {
                Log.debug( e.getMessage());
            }
        }
    }
}
